package com.powerbank.usercenter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.powerbank.common.exception.BusinessException;
import com.powerbank.common.mq.EventPublisher;
import com.powerbank.common.result.PageResult;
import com.powerbank.common.query.PageQuery;
import com.powerbank.common.utils.JwtUtils;
import com.powerbank.common.event.user.UserRegisteredEvent;
import com.powerbank.usercenter.dto.UserRegisterDTO;
import com.powerbank.usercenter.dto.UserLoginDTO;
import com.powerbank.usercenter.dto.UserUpdateDTO;
import com.powerbank.usercenter.entity.User;
import com.powerbank.usercenter.mapper.UserMapper;
import com.powerbank.usercenter.service.UserService;
import com.powerbank.usercenter.service.SmsService;
import com.powerbank.usercenter.vo.UserInfoVO;
import com.powerbank.usercenter.vo.UserLoginVO;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户服务实现类
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private SmsService smsService;

    @Autowired
    private EventPublisher eventPublisher;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private JwtUtils jwtUtils;

    private static final String USER_POINTS_KEY = "user:points:";
    private static final String USER_CACHE_KEY = "user:info:";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserLoginVO register(UserRegisterDTO registerDTO) {
        // 检查手机号是否已注册
        if (checkUserExists(registerDTO.getPhone())) {
            throw new BusinessException("手机号已注册");
        }

        // 验证短信验证码
        if (!smsService.verifySmsCode(registerDTO.getPhone(), registerDTO.getSmsCode(), "register")) {
            throw new BusinessException("短信验证码错误");
        }

        // 创建用户
        User user = new User();
        user.setPhone(registerDTO.getPhone());
        user.setPassword(BCrypt.hashpw(registerDTO.getPassword()));
        user.setNickname(generateNickname(registerDTO.getPhone()));
        user.setStatus(1); // 正常状态
        user.setRegisterSource(registerDTO.getRegisterSource());
        user.setCreatedTime(LocalDateTime.now());
        user.setUpdatedTime(LocalDateTime.now());

        userMapper.insert(user);

        // 发布用户注册事件
        UserRegisteredEvent event = new UserRegisteredEvent(
                user.getId(), 
                user.getPhone(), 
                registerDTO.getRegisterSource()
        );
        event.setInviteCode(registerDTO.getInviteCode());
        event.setNickname(user.getNickname());
        eventPublisher.publishUserEvent(event);

        // 清除验证码
        smsService.clearSmsCode(registerDTO.getPhone(), "register");

        // 生成登录token
        String token = jwtUtils.generateToken(user.getId(), user.getPhone(), "USER");
        String refreshToken = jwtUtils.generateRefreshToken(user.getId(), user.getPhone(), "USER");

        UserLoginVO loginVO = new UserLoginVO();
        loginVO.setUserId(user.getId());
        loginVO.setUsername(user.getPhone());
        loginVO.setNickname(user.getNickname());
        loginVO.setToken(token);
        loginVO.setRefreshToken(refreshToken);
        loginVO.setFirstLogin(true);

        log.info("用户注册成功: userId={}, phone={}", user.getId(), user.getPhone());
        return loginVO;
    }

    @Override
    public UserLoginVO login(UserLoginDTO loginDTO) {
        User user = getUserByPhoneInternal(loginDTO.getPhone());
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        if (user.getStatus() != 1) {
            throw new BusinessException("账号已被禁用");
        }

        // 验证密码或短信验证码
        boolean loginSuccess = false;
        if (StrUtil.isNotBlank(loginDTO.getPassword())) {
            // 密码登录
            if (BCrypt.checkpw(loginDTO.getPassword(), user.getPassword())) {
                loginSuccess = true;
            }
        } else if (StrUtil.isNotBlank(loginDTO.getSmsCode())) {
            // 短信验证码登录
            if (smsService.verifySmsCode(loginDTO.getPhone(), loginDTO.getSmsCode(), "login")) {
                loginSuccess = true;
                smsService.clearSmsCode(loginDTO.getPhone(), "login");
            }
        }

        if (!loginSuccess) {
            throw new BusinessException("登录失败，请检查密码或验证码");
        }

        // 更新最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        user.setLastLoginIp(loginDTO.getLoginIp());
        userMapper.updateById(user);

        // 生成登录token
        String token = jwtUtils.generateToken(user.getId(), user.getPhone(), "USER");
        String refreshToken = jwtUtils.generateRefreshToken(user.getId(), user.getPhone(), "USER");

        UserLoginVO loginVO = new UserLoginVO();
        loginVO.setUserId(user.getId());
        loginVO.setUsername(user.getPhone());
        loginVO.setNickname(user.getNickname());
        loginVO.setToken(token);
        loginVO.setRefreshToken(refreshToken);
        loginVO.setFirstLogin(false);

        log.info("用户登录成功: userId={}, phone={}", user.getId(), user.getPhone());
        return loginVO;
    }

    @Override
    public void logout(String token) {
        if (StrUtil.isNotBlank(token) && token.startsWith("Bearer ")) {
            String actualToken = token.substring(7);
            // 将token加入黑名单
            String blacklistKey = "token:blacklist:" + actualToken;
            redisTemplate.opsForValue().set(blacklistKey, "1", 24, TimeUnit.HOURS);
        }
        log.info("用户退出登录");
    }

    @Override
    public UserInfoVO getUserInfo(Long userId) {
        // 先从缓存获取
        String cacheKey = USER_CACHE_KEY + userId;
        Object cached = redisTemplate.opsForHash().entries(cacheKey);
        
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        UserInfoVO userInfo = BeanUtil.copyProperties(user, UserInfoVO.class);
        userInfo.setPoints(getUserPoints(userId));

        // 缓存用户信息
        redisTemplate.opsForValue().set(cacheKey, userInfo.toString(), 30, TimeUnit.MINUTES);

        return userInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserInfo(Long userId, UserUpdateDTO updateDTO) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 更新用户信息
        User updateUser = BeanUtil.copyProperties(updateDTO, User.class);
        updateUser.setId(userId);
        updateUser.setUpdatedTime(LocalDateTime.now());

        userMapper.updateById(updateUser);

        // 清除缓存
        String cacheKey = USER_CACHE_KEY + userId;
        redisTemplate.delete(cacheKey);

        log.info("更新用户信息成功: userId={}", userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePassword(Long userId, String oldPassword, String newPassword) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        if (!BCrypt.checkpw(oldPassword, user.getPassword())) {
            throw new BusinessException("原密码错误");
        }

        // 更新密码
        user.setPassword(BCrypt.hashpw(newPassword));
        user.setUpdatedTime(LocalDateTime.now());
        userMapper.updateById(user);

        log.info("用户修改密码成功: userId={}", userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bindPhone(Long userId, String phone, String smsCode) {
        // 验证短信验证码
        if (!smsService.verifySmsCode(phone, smsCode, "bind")) {
            throw new BusinessException("短信验证码错误");
        }

        // 检查手机号是否已被其他用户绑定
        User existUser = getUserByPhoneInternal(phone);
        if (existUser != null && !existUser.getId().equals(userId)) {
            throw new BusinessException("该手机号已被其他用户绑定");
        }

        // 更新手机号
        User user = new User();
        user.setId(userId);
        user.setPhone(phone);
        user.setUpdatedTime(LocalDateTime.now());
        userMapper.updateById(user);

        // 清除验证码和缓存
        smsService.clearSmsCode(phone, "bind");
        String cacheKey = USER_CACHE_KEY + userId;
        redisTemplate.delete(cacheKey);

        log.info("用户绑定手机号成功: userId={}, phone={}", userId, phone);
    }

    @Override
    public PageResult<UserInfoVO> getUserPage(PageQuery pageQuery, String keyword) {
        Page<User> page = new Page<>(pageQuery.getPage(), pageQuery.getSize());
        
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotBlank(keyword)) {
            queryWrapper.and(wrapper -> wrapper
                    .like(User::getPhone, keyword)
                    .or().like(User::getNickname, keyword)
                    .or().like(User::getEmail, keyword));
        }
        queryWrapper.orderByDesc(User::getCreatedTime);

        IPage<User> userPage = userMapper.selectPage(page, queryWrapper);
        
        List<UserInfoVO> userInfoList = userPage.getRecords().stream()
                .map(user -> {
                    UserInfoVO userInfo = BeanUtil.copyProperties(user, UserInfoVO.class);
                    userInfo.setPoints(getUserPoints(user.getId()));
                    return userInfo;
                })
                .collect(Collectors.toList());

        return new PageResult<>(userInfoList, userPage.getTotal(), userPage.getCurrent(), userPage.getSize());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void disableUser(Long userId) {
        User user = new User();
        user.setId(userId);
        user.setStatus(2); // 禁用状态
        user.setUpdatedTime(LocalDateTime.now());
        userMapper.updateById(user);

        // 清除缓存
        String cacheKey = USER_CACHE_KEY + userId;
        redisTemplate.delete(cacheKey);

        log.info("禁用用户成功: userId={}", userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void enableUser(Long userId) {
        User user = new User();
        user.setId(userId);
        user.setStatus(1); // 正常状态
        user.setUpdatedTime(LocalDateTime.now());
        userMapper.updateById(user);

        // 清除缓存
        String cacheKey = USER_CACHE_KEY + userId;
        redisTemplate.delete(cacheKey);

        log.info("启用用户成功: userId={}", userId);
    }

    @Override
    public UserInfoVO getUserByPhone(String phone) {
        User user = getUserByPhoneInternal(phone);
        if (user == null) {
            return null;
        }
        return BeanUtil.copyProperties(user, UserInfoVO.class);
    }

    @Override
    public boolean checkUserExists(String phone) {
        return getUserByPhoneInternal(phone) != null;
    }

    @Override
    public void addUserPoints(Long userId, Integer points, String reason) {
        String key = USER_POINTS_KEY + userId;
        redisTemplate.opsForValue().increment(key, points);
        redisTemplate.expire(key, 365, TimeUnit.DAYS);
        
        log.info("用户积分增加: userId={}, points={}, reason={}", userId, points, reason);
    }

    @Override
    public Integer getUserPoints(Long userId) {
        String key = USER_POINTS_KEY + userId;
        String points = redisTemplate.opsForValue().get(key);
        return points != null ? Integer.parseInt(points) : 0;
    }

    /**
     * 根据手机号查询用户（内部方法）
     */
    private User getUserByPhoneInternal(String phone) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhone, phone);
        return userMapper.selectOne(queryWrapper);
    }

    /**
     * 生成用户昵称
     */
    private String generateNickname(String phone) {
        return "用户" + phone.substring(phone.length() - 4);
    }
}